package com.cs410.collabwriting.main;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;

import com.cs410.collabwriting.tools.DatabaseManager;
import com.cs410.collabwriting.tools.queryTools.DatabaseTable;
import com.cs410.collabwriting.tools.queryTools.QueryBuilder;
import com.cs410.collabwriting.tools.queryTools.QueryCondition;
import com.cs410.collabwriting.tools.queryTools.QueryType;
import com.cs410.collabwriting.tools.queryTools.tables.POST_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.STORYLISTNAME_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.STORYLIST_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.STORY_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.SUBSCRIBED_STORY_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.SUBSCRIBED_USER_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.USER_TABLE;

public class UserManager implements Manager {

	private static UserManager s_userManager = null;
	private HashMap<String,User> m_userList = null; // <UserID, UserObject>
	
	private UserManager() {
		m_userList = new HashMap<String, User>();
	}
	
	// LOL LAZY LOGIN.
	public static User login( String userID, String password ) {
		MessageDigest d = null;
		try {
			d = java.security.MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		String encryptedPassword = "";
		if (d != null) {
			encryptedPassword = new String ( d.digest(password.getBytes()));
		}
		
		String returnedPassword = DatabaseManager.executeExactQuery(
			new QueryBuilder(QueryType.SELECTION)
			.addColumns(USER_TABLE.PASSWORD)
			.addTables(DatabaseTable.USER_TABLE)
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar(userID) )
			.value() );
		String isActivated = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.ACTIVATEKEY)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar(userID) )
				.value() );
		
		if( returnedPassword != null && returnedPassword.compareToIgnoreCase( encryptedPassword ) == 0
				&& isActivated != null && isActivated.compareToIgnoreCase( "0" ) == 0) {
			return new User(userID );
		} 
		return null;
	}
	
	public User addUser( String userID, String password, String email ) {
		MessageDigest d = null;
		try {
			d = java.security.MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		String encryptedPassword = "";
		if (d != null) {
			encryptedPassword = new String ( d.digest(password.getBytes()));
		}
		int addedUser = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.INSERT)
			.addColumns(USER_TABLE.USERID, USER_TABLE.PASSWORD, USER_TABLE.EMAIL)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues( QueryBuilder.toSQLChar(userID), QueryBuilder.toSQLChar(encryptedPassword), email )
			.value() );
		if (addedUser > 0) {
			User user = new User(userID);
			user.setActivateKey(email);
			return user;
		}
		return null;
//		return addedUser > 0 ? new User(userID) : null;
	}
	
	public User getUser( String userID ) {
		User user = m_userList.get( userID );
		if( user == null ) { 
			String retUserID = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns("COUNT(*)")
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar(userID) )
				.value() );
			if( retUserID != null && Integer.parseInt( retUserID ) > 0) {
				user = new User(userID);
				m_userList.put( userID, user );
			} else {
				return null;
			}
		} else {
			user.update();
		}
		return user;
	}
	
	public ArrayList<Story> getUserContributedStories( String userID ) {
		ArrayList<Story> storyList = new ArrayList<Story>();
		ArrayList<String[]> queryResults = DatabaseManager.executeQuery(
			new QueryBuilder(QueryType.SELECTION)
			.addColumns( POST_TABLE.STORYID )
			.addTables( DatabaseTable.POST_TABLE )
			.addConditions( QueryCondition.AND, POST_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar( userID ) )
			.setDistinct( true )
			.value() );
		if( queryResults != null ) {
			StoryManager sM = StoryManager.getInstance();
			for( int i = 0; i < queryResults.size(); i++ ) {
				int storyId = Integer.parseInt( queryResults.get( i )[0] );
				storyList.add( sM.getStoryByID( storyId ) );
			}	
		}
		return storyList;
	}
	
	
	public static UserManager getInstance() {
		if( s_userManager == null ) {
			s_userManager = new UserManager();
		}
		return s_userManager;
	}
	
	@Override
	public void update() {
		m_userList = new HashMap<String, User>();
	}

	@Override
	public ArrayList<DatabaseObjects> getAll() {
		ArrayList<DatabaseObjects> userList = new ArrayList<DatabaseObjects>();
		ArrayList<String[]> userIDList;
		
		userIDList = DatabaseManager.executeQuery(
			new QueryBuilder(QueryType.SELECTION)
			.addColumns( USER_TABLE.USERID )
			.addTables( DatabaseTable.USER_TABLE )
			.setDistinct( true )
			.value() );
		
		for( int i = 0; i < userIDList.size(); i++ ) {
			User user = new User( userIDList.get( i )[0] );
			m_userList.put( user.getUserID() , user );
			userList.add( user );
		}
		return userList;
	}
	
	public boolean checkIfUserHasList(String userId, String listId) {
	    ArrayList<String[]> check = DatabaseManager.executeQuery(new QueryBuilder(QueryType.SELECTION)
            .addColumns (STORYLISTNAME_TABLE.LISTID)
            .addTables(DatabaseTable.STORYLISTNAME_TABLE)
            .addConditions(QueryCondition.AND, STORYLISTNAME_TABLE.USERID, QueryCondition.EQ, userId)
            .addConditions(QueryCondition.AND, STORYLISTNAME_TABLE.LISTID, QueryCondition.EQ, listId)
            .value() );
	    
	    if (check == null || check.size() == 0) {
	        return false;
	    }
	    
	    return true;
	}

    public ArrayList<DatabaseObjects> getSubscribedStories(String userID) {
        ArrayList<DatabaseObjects> storyList = new ArrayList<DatabaseObjects>();
        StoryManager sm = StoryManager.getInstance();
        
        ArrayList<String[]> subscribedStories = DatabaseManager.executeQuery(
            new QueryBuilder(QueryType.SELECTION)
            .addColumns(DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID)
            .addTables(DatabaseTable.STORY_TABLE, DatabaseTable.POST_TABLE, DatabaseTable.SUBSCRIBED_STORY_TABLE)
            .addConditions(QueryCondition.AND, DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID, QueryCondition.EQ, DatabaseTable.POST_TABLE + "." + POST_TABLE.STORYID )
            .addConditions(QueryCondition.AND, DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID, QueryCondition.EQ, DatabaseTable.SUBSCRIBED_STORY_TABLE + "." + SUBSCRIBED_STORY_TABLE.STORYID)
            .addConditions(QueryCondition.AND, DatabaseTable.SUBSCRIBED_STORY_TABLE + "." + SUBSCRIBED_STORY_TABLE.USERID, QueryCondition.EQ, userID)
            .addAdditionalCriteria("GROUP BY STORY_TABLE.STORYID")
            .addAdditionalCriteria("ORDER BY POST_TABLE.TIMESTAMP ASC")
            .value() ); 
        
        if( subscribedStories != null ) {
            for (int i= 0; i < subscribedStories.size(); i++){
                int IDstory = Integer.valueOf(subscribedStories.get(i)[0]);
                Story story;
                story = sm.getStoryByID(IDstory);
                storyList.add(story);
            }
        }
        
        return storyList;
    }

    public ArrayList<DatabaseObjects> getSubscribedUsers(String userID) {
        ArrayList<DatabaseObjects> userList = new ArrayList<DatabaseObjects>();
        
        ArrayList<String[]> subscribedUsers = DatabaseManager.executeQuery(
            new QueryBuilder(QueryType.SELECTION)
            .addColumns(SUBSCRIBED_USER_TABLE.FAVUSERID)
            .addTables(DatabaseTable.SUBSCRIBED_USER_TABLE)
            .addConditions(QueryCondition.AND, DatabaseTable.SUBSCRIBED_USER_TABLE + "." + SUBSCRIBED_USER_TABLE.USERID, QueryCondition.EQ, userID)
            .value() ); 
        
        if (subscribedUsers != null) {
            for (String[] subUser : subscribedUsers) {
                User user = UserManager.getInstance().getUser(subUser[0]);
                userList.add(user);
            }
        }
        
        return userList;
    }

    public boolean unsubscribeFromUser(String userId, String faveUserId) {
        int deleted = DatabaseManager.executeUpdate(
            new QueryBuilder(QueryType.DELETE)
            .addTables(DatabaseTable.SUBSCRIBED_USER_TABLE)
            .addConditions(QueryCondition.AND, SUBSCRIBED_USER_TABLE.USERID, QueryCondition.EQ, userId)
            .addConditions(QueryCondition.AND, SUBSCRIBED_USER_TABLE.FAVUSERID, QueryCondition.EQ, faveUserId));
        
        if (deleted > 0)
            return true;
        
        return false;
    }

    public boolean unsubscribeFromStory(String userId, String storyId) {
        int deleted = DatabaseManager.executeUpdate(
            new QueryBuilder(QueryType.DELETE)
            .addTables(DatabaseTable.SUBSCRIBED_STORY_TABLE)
            .addConditions(QueryCondition.AND, SUBSCRIBED_STORY_TABLE.USERID, QueryCondition.EQ, userId)
            .addConditions(QueryCondition.AND, SUBSCRIBED_STORY_TABLE.STORYID, QueryCondition.EQ, storyId));
        
        if (deleted > 0)
            return true;
        
        return false;
    }
}
